﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.VersionControl.Client;
using System.IO;
using Microsoft.TeamFoundation.Client;
using System.Xml.Linq;
using System.Text.RegularExpressions;

namespace ExceptionConverter
{
    class Program
    {
        private static TeamFoundationServer tfs = null;
        private static VersionControlServer versionControlServer = null;
        private static Workspace workspace = null;

        private const string RootPath = @"C:\DSEPROJ\";

        private const string COMMON_EXCEPTIONS_HINT_PATH = @"\Compilados\Sinacor\Common\Infra\Sinacor.Infra.Common.Exceptions.dll";
        private const string UI_RESOURCES_HINT_PATH = @"\Compilados\PSE\Framework\v3\Interface Layer\PSE.Framework.UI.Resources.dll";
        private const string UI_SETTINGS_HINT_PATH = @"\Compilados\PSE\Framework\v3\Interface Layer\PSE.Framework.UI.Settings.dll";


        static void Main(string[] args)
        {
            List<string> branchs = new List<string>() 
            { 
                @"NET\DEV\CDSB\VN", 
                @"NET\MAIN\VC\VC"
                //@"NET\MAIN-SCN-21657\VC\VC"
            };

            List<string> filters = new List<string>() { "*.config", "*.csproj", "*.cs" };
            List<string> folders = new List<string>() 
            { 
                //"iMercado", 
                //"SinaCCO", 
                //"SinaCLI", 
                //"SinaCOMP", 
                //"SinaCOMU", 
                //"SinaIAPO", 
                "SinaOMF", 
                "SinaORD", 
                //"SinaSOL", 
                //"SinaTES", 
                //"SinaCTP", 
                //"SinaCA"
            };


            int count = 0, countTotal = 0;

            tfs = TeamFoundationServerFactory.GetServer("utilcorp05");
            versionControlServer = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
            workspace = versionControlServer.GetWorkspace(RootPath);

            foreach (string folder in Directory.GetDirectories(RootPath))
            {
                if (folders.Contains(Path.GetFileName(folder)))
                {
                    foreach (string branch in branchs)
                    {
                        string path = folder + @"\" + branch;

                        if (Directory.Exists(path))
                        {
                            Console.WriteLine("Alterando pasta: " + path);

                            string[] files = new string[0];
                            foreach (string filter in filters)
                                files = Directory.GetFiles(path, filter, SearchOption.AllDirectories).Concat(files).ToArray();

                            foreach (string file in files)
                            {
                                bool fileUpdated = false;

                                XDocument project = null;
                                string content = null;

                                switch (Path.GetExtension(file).ToLower())
                                {
                                    case @".config":
                                        project = XDocument.Load(file);
                                        fileUpdated = RemoveConfigExceptions(project);
                                        break;

                                    case @".csproj":
                                        project = XDocument.Load(file);
                                        fileUpdated = AddProjectReference(project, file);
                                        break;

                                    case @".cs":
                                        string originalContent = File.ReadAllText(file, Encoding.Default);
                                        content = AddUsingReference(file, originalContent);
                                        fileUpdated = content != originalContent;

                                        CheckExceptions(file, content);

                                        break;

                                    default:
                                        break;
                                }

                                if (fileUpdated)
                                {
                                    Console.WriteLine(file);
                                    count++;

                                    // checkout e salvar
                                    workspace.PendEdit(file);

                                    if (project != null)
                                        project.Save(file);

                                    if (!string.IsNullOrEmpty(content))
                                        File.WriteAllText(file, content, Encoding.UTF8);
                                }

                            }

                            countTotal += count;

                            Console.WriteLine("Arquivos alterados: " + count);
                            Console.WriteLine();

                            count = 0;
                        }
                    }
                }
            }

            Console.WriteLine("Fim do processamento, arquivos alterados: " + countTotal);
            Console.ReadKey();
        }

        private static bool RemoveConfigExceptions(XDocument project)
        {
            bool fileUpdated = false;

            var exceptionHandlings = from r in project.Descendants()
                                     where
                                         r.Name.LocalName == "exceptionHandling" ||
                                         (
                                            r.Name.LocalName == "section" &&
                                            r.Attribute("name") != null &&
                                            r.Attribute("name").Value == "exceptionHandling"
                                         )
                                     select r;

            if (exceptionHandlings.Count() > 0)
            {
                for (int i = exceptionHandlings.ToList().Count - 1; i >= 0; i--)
                {
                    exceptionHandlings.ToList()[i].Remove();
                }

                fileUpdated = true;
            }

            return fileUpdated;
        }

        private static bool AddProjectReference(XDocument project, string file)
        {
            bool fileUpdated = false;

            var uiServiceExceptions = from r in project.Descendants()
                                      where
                                         r.Name.LocalName == "Reference" &&
                                         r.Attribute("Include") != null &&
                                         (
                                             r.Attribute("Include").Value.StartsWith("Sinacor.Infra.Service.ExceptionHandling") ||
                                             r.Attribute("Include").Value.StartsWith("Sinacor.Infra.UI.Exceptions")
                                         )
                                      select r;

            var commonExceptions = from r in project.Descendants()
                                   where
                                      r.Name.LocalName == "Reference" &&
                                      r.Attribute("Include") != null &&
                                      r.Attribute("Include").Value.StartsWith("Sinacor.Infra.Common.Exceptions")
                                   select r;

            if (uiServiceExceptions.Count() > 0 && commonExceptions.Count() == 0)
            {
                string ns = uiServiceExceptions.First().Name.NamespaceName;

                XElement commonExceptionReference = new XElement(uiServiceExceptions.First());
                commonExceptionReference.SetAttributeValue("Include", "Sinacor.Infra.Common.Exceptions, Version=1.0.0.0, Culture=neutral, PublicKeyToken=9b0dac35dd685e79, processorArchitecture=x86");
                commonExceptionReference.SetElementValue(XName.Get("SpecificVersion", ns), "False");
                commonExceptionReference.SetElementValue(XName.Get("HintPath", ns), GetHintPath(file, COMMON_EXCEPTIONS_HINT_PATH));
                commonExceptionReference.SetElementValue(XName.Get("Private", ns), "False");

                uiServiceExceptions.First().Parent.Add(commonExceptionReference);

                fileUpdated = true;
            }



            var uiResources = from r in project.Descendants()
                              where
                                  r.Name.LocalName == "Reference" &&
                                  r.Attribute("Include") != null &&
                                  r.Attribute("Include").Value.StartsWith("Sinacor.Infra.UI.Resources")
                              select r;

            if (uiResources.Count() > 0)
            {
                XElement uiResource = uiResources.First();

                string ns = uiResource.Name.NamespaceName;

                uiResource.SetAttributeValue("Include", "PSE.Framework.UI.Resources, Version=4.0.0.0, Culture=neutral, PublicKeyToken=d12c15b5d58bc696, processorArchitecture=x86");
                uiResource.SetElementValue(XName.Get("SpecificVersion", ns), "True");
                uiResource.SetElementValue(XName.Get("HintPath", ns), GetHintPath(file, UI_RESOURCES_HINT_PATH));
                uiResource.SetElementValue(XName.Get("Private", ns), "False");

                fileUpdated = true;
            }



            var uiSettings = from r in project.Descendants()
                             where
                                  r.Name.LocalName == "Reference" &&
                                  r.Attribute("Include") != null &&
                                  r.Attribute("Include").Value.StartsWith("Sinacor.Infra.UI.Settings")
                             select r;

            var frameworkSettings = from r in project.Descendants()
                                    where
                                          r.Name.LocalName == "Reference" &&
                                          r.Attribute("Include") != null &&
                                          r.Attribute("Include").Value.StartsWith("PSE.Framework.UI.Settings")
                                    select r;

            if (uiSettings.Count() > 0 && frameworkSettings.Count() == 0)
            {
                string ns = uiSettings.First().Name.NamespaceName;

                XElement uiSettingReference = new XElement(uiSettings.First());
                uiSettingReference.SetAttributeValue("Include", "PSE.Framework.UI.Settings, Version=4.0.0.0, Culture=neutral, PublicKeyToken=d12c15b5d58bc696, processorArchitecture=x86");
                uiSettingReference.SetElementValue(XName.Get("SpecificVersion", ns), "True");
                uiSettingReference.SetElementValue(XName.Get("HintPath", ns), GetHintPath(file, UI_SETTINGS_HINT_PATH));
                uiSettingReference.SetElementValue(XName.Get("Private", ns), "False");

                uiSettings.First().Parent.Add(uiSettingReference);

                fileUpdated = true;
            }

            return fileUpdated;
        }

        private static string AddUsingReference(string file, string originalContent)
        {
            string content = originalContent;

            if (file.ToLower().Contains(@"\ui\"))
            {
                // UI
                content = content.Replace("using Sinacor.Infra.UI.Exceptions;", "using Sinacor.Infra.Common.Exceptions;");
                content = content.Replace("Sinacor.Infra.UI.Resources", "PSE.Framework.UI.Resources");

                if (content.Contains("UI.Exceptions.") &&
                    !content.Contains("using Sinacor.Infra.Common.Exceptions;"))
                {
                    content = "using Sinacor.Infra.Common.Exceptions;" + Environment.NewLine +
                              content;
                }

                content = content.Replace("Sinacor.Infra.UI.Exceptions.", "");
                content = content.Replace("Infra.UI.Exceptions.", "");
                content = content.Replace("UI.Exceptions.", "");
            }
            else
            {
                // Serviços
                if (content.Contains("using Sinacor.Infra.Service.ExceptionHandling;") &&
                    !content.Contains("using Sinacor.Infra.Common.Exceptions;"))
                {
                    content = content.Replace("using Sinacor.Infra.Service.ExceptionHandling;",
                                              "using Sinacor.Infra.Common.Exceptions;" + Environment.NewLine +
                                              "using Sinacor.Infra.Service.ExceptionHandling;");
                }

                if (content.Contains("ExceptionHandling."))
                {
                    if (!content.Contains("using Sinacor.Infra.Service.ExceptionHandling;"))
                    {
                        content = "using Sinacor.Infra.Service.ExceptionHandling;" + Environment.NewLine +
                                  content;
                    }
                    if (!content.Contains("using Sinacor.Infra.Common.Exceptions;"))
                    {
                        content = "using Sinacor.Infra.Common.Exceptions;" + Environment.NewLine +
                                  content;
                    }
                }

                content = content.Replace("Sinacor.Infra.Service.ExceptionHandling.", "");
                content = content.Replace("Infra.Service.ExceptionHandling.", "");
                content = content.Replace("Service.ExceptionHandling.", "");
                content = content.Replace("ExceptionHandling.", "");

                content = content.Replace("new ErrorMessage().ConvertXmlArgumentsToParameters", "ErrorMessage.ConvertXmlArgumentsToParameters");
            }

            return content;
        }

        private static void CheckExceptions(string file, string content)
        {
            if ((file.ToLower().Contains(@"\ui\") &&
                  (
                    content.Contains(@"BusinessException(") ||
                    content.Contains(@"ValidationException(") ||
                    content.Contains(@"AddError(")
                  )
                ) ||
                content.Contains(@"PSE.ProcessManager.Common.Logging")
               )
            {
                string[] lines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None);
                StringBuilder message = new StringBuilder();

                for (int i = 0; i < lines.Length; i++)
                {
                    if (lines[i].Contains(@"BusinessException(") ||
                        lines[i].Contains(@"ValidationException(") ||
                        lines[i].Contains(@"AddError(") ||
                        lines[i].Contains(@"PSE.ProcessManager.Common.Logging"))
                    {
                        message.AppendFormat("Line: {0}\t '{1}'", i, lines[i].Trim());
                        message.AppendLine();
                    }
                }

                if (message.Length > 0)
                {
                    message.Insert(0, Environment.NewLine);
                    Log(message.ToString(), file);
                }
            }
        }

        private static string GetHintPath(string projectPath, string hintPath)
        {
            // Calcula quantas pastas deve descer até o workspace
            int levels = Path.GetDirectoryName(projectPath).Replace(RootPath, "").Split('\\').Length;

            for (int i = 0; i < levels; i++)
            {
                hintPath = (i == 0 ? ".." : "..\\") + hintPath;
            }

            return hintPath;
        }

        const string LogFile = @"\ExceptionConverter.xml";

        private static void Log(string message, string file)
        {
            XDocument logs;

            if (File.Exists(LogFile))
                logs = XDocument.Load(LogFile);
            else
                logs = new XDocument(new XElement("Logs"));

            XElement log = logs.Root.Elements().FirstOrDefault(e => e.Attribute("File").Value == file);

            if (log == null)
            {
                log = new XElement("Log");
                log.SetAttributeValue("File", file);
                logs.Root.Add(log);
            }

            log.SetElementValue("Message", message);

            logs.Save(LogFile);
        }
    }
}
